package concreto.redeneural;

import java.util.ArrayList;
import java.util.List;

import comportamento.neuronio.INeuronio;
import comportamento.neuronio.INeuronioAbstrato;
import comportamento.redeneural.ICamadaNeural;
import comportamento.redeneural.ICamadaNeuralFeedFoward;
import comportamento.redeneural.IRedeNeural;

import concreto.neuronio.NeuronioGenerico;
import concreto.neuronio.NeuronioMP;

public class RedeNeuralFeedFoward implements IRedeNeural{

	private List<ICamadaNeural> camadas = new ArrayList<ICamadaNeural>();
	private int indiceCamadas = 0;

	public INeuronio criarNeuronio() {

		//Neuronio neuronio = new NeuronioMP(); 
		return null;//neuronio;
	}

	public INeuronio criarNeuronioGenerico(double limiar, int funcaoAtivacao) {

		INeuronio neuronio = new NeuronioGenerico(limiar); 
		neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);

		return neuronio;
	}    

	public INeuronio criarNeuronioMP(double limiar) {

		INeuronio neuronio = new NeuronioMP(limiar); 
		neuronio.selecionarFuncaoAtivacao(INeuronioAbstrato.ATIVACAO_DEGRAU);

		return neuronio;
	}

	public INeuronio criarNeuronio(int funcaoAtivacao) {

		INeuronio neuronio = new NeuronioMP(0);
		neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);

		return neuronio;
	}

	public INeuronio criarNeuronio(Class<? extends INeuronio> classeConcreta) {

		INeuronio neuronio = null;
		try {

			neuronio = classeConcreta.newInstance();

		} catch (InstantiationException e) {

			e.printStackTrace();
		} catch (IllegalAccessException e) {

			e.printStackTrace();
		} 

		return neuronio;
	}

	public INeuronio criarNeuronio(Class<? extends INeuronio> classeConcreta, int funcaoAtivacao) {

		INeuronio neuronio = null;

		try {

			neuronio = classeConcreta.newInstance();
			neuronio.selecionarFuncaoAtivacao(funcaoAtivacao);

		} catch (InstantiationException e) {

			e.printStackTrace();
		} catch (IllegalAccessException e) {

			e.printStackTrace();
		} 

		return neuronio;
	}


	public double[] estimular(double[] estimulos) {

		double[] aux = estimulos;

		for(ICamadaNeural c : camadas) {
			aux = c.estimular(aux);
		}

		return aux;
	}    

	public void criarCamadaNeural(int quantidadeNeuronios, int tipoNeuronio) {

		ICamadaNeuralFeedFoward camadaNeural = new CamadaNeuralFeedFowardMP();

		for(int i = 0; i < quantidadeNeuronios; i++) {

			camadaNeural.adicionarNeuronio(criarNeuronioGenerico(0.0, tipoNeuronio));
		}

		camadas.add(camadaNeural);
	}

	public void criarCamadasNeurais(int quantidadeCamadas, int[] quantidadeNeuronios, 
			int[] tiposNeuronios, double[] limiares) 
	throws IllegalArgumentException {

		if(quantidadeCamadas != quantidadeNeuronios.length || quantidadeCamadas != tiposNeuronios.length) {

			throw new IllegalArgumentException("Quantidade de camadas deve ser igual aos elementos dos vetores de quantidade e tipo de neuronios!");
		}

		for(int i = 0; i < quantidadeCamadas; i++) {
			criarCamadaNeural(quantidadeNeuronios[i], tiposNeuronios[i]);
		}
	}    

	public void adicionarCamadaNeural(ICamadaNeural camada) {

		camadas.add(camada);
	}

	public ICamadaNeural camadaNeural() {

		return camadas.get(indiceCamadas);
	}


	public ICamadaNeural proximaCamada() {

		indiceCamadas++;
		return camadaNeural();
	}    

	public ICamadaNeural primeiraCamada() {

		indiceCamadas = 0;
		return camadaNeural();	
	}

	public ICamadaNeural ultimaCamada() {

		indiceCamadas = camadas.size() -1;
		return camadaNeural();
	}

	public void removerCamadaNeural(ICamadaNeural camada) {

		camadas.remove(camada);
	}

	public boolean temCamadas() {

		return (indiceCamadas < camadas.size());
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {

		StringBuffer b = new StringBuffer("");
		int contCamadas = 1;

		for(ICamadaNeural camada : camadas) {

			b.append("\n\n[Camada " + (contCamadas++) + "]");
			b.append(camada);
		}

		return b.toString();
	}

	public void treinar(double[] entradas, double[] saidas) {
		for(ICamadaNeural camada : camadas) {
			camada.treinar(entradas, saidas);
		}
	}

}
